﻿/// <reference path="waa.d.ts" />
/// <reference path="filesystem.d.ts" />
module fireemblum {

    interface Point {
        x: number;
        y: number;
    }

    enum GameState {
        Normal,
        Thinking,
        Moving,
        Deciding,
        Battling,
    }

    class Game {
        private root: HTMLCanvasElement;
        private context: CanvasRenderingContext2D;
        private counter: number;
        private fps_previous: number;
        private fps_counter: number;
        private fps: number;
        private tiles: ImageData;
        private init_time: number;

        private game_state: GameState;
        private selected_hero: number;

        private characters: HTMLImageElement[];
        private bg_tile: HTMLImageElement[];


        private cc =
        [
            {
                name: '玛尔斯',
                pic: 0,
                x: 6,
                y: 6
            },
            {
                name: '囧',
                pic: 1,
                x: 6,
                y: 8
            }, {
                name: '大波流',
                pic: 2,
                x: 4,
                y: 6
            }, {
                name: '文森特',
                pic: 2,
                x: 4,
                y: 8
            }, {
                name: '海伦',
                pic: 2,
                x: 3,
                y: 8
            }
        ];


        public constructor(root: HTMLCanvasElement) {
            this.root = root;
            this.context = root.getContext('2d');
        }

        public run(): void {
            this.init();
            this.running();
            //this.cleanup();
        }

        private init(): void {
            console.info('Game is initializing');

            this.game_state = GameState.Normal;

            // register mouse event
            this.root.oncontextmenu = (e) => { return false };
            this.root.addEventListener('mousedown',(e) => { this.onMouseDown(e) }, false);

            // load bg tile
            this.bg_tile = [];
            for (var i = 0; i < 9; i++) {
                var bg = new Image();
                bg.src = `res/tile/b${i}.svg`;
                this.bg_tile.push(bg);
            }

            // load characters
            this.characters = [];
            for (var i = 0; i < 3; i++) {
                var cc = new Image();
                cc.src = `res/character/0${i}.svg`;
                this.characters.push(cc);
            }

            // load and start bg music
            this.initMusic();

            this.init_time = new Date().getTime();
            this.fps = 60;
            this.fps_counter = 0;
            this.fps_previous = new Date().getTime();
            this.counter = 0;
            window.setInterval(() => {
                this.counter++;

                this.fps = this.fps_counter;
                this.fps_counter = 0;
            }, 1000);
        }

        private onMouseDown(e: MouseEvent): void {
            if (e.button == 0) {
                e.cancelBubble = true;
                this.onLeftInput(this.getInputTile(e.x, e.y));
            } else if (e.button == 2) {
                e.cancelBubble = true;
                this.onRightInput(this.getInputTile(e.x, e.y));
            }
        }

        private onLeftInput(e: Point): void {
            console.log(`left input -> ${e.x}:${e.y}`);

            if (this.game_state != GameState.Normal) {
                // TODO: not impl
                return;
            }

            var selected = -1;
            for (var i = 0; i < this.cc.length; i++) {
                if (this.cc[i].x == e.x && this.cc[i].y == e.y) {
                    selected = i;
                    break;
                }
            }

            if (selected != -1) {
                this.selected_hero = selected;
                this.game_state = GameState.Deciding;
            }
        }

        private onRightInput(e: Point): void {
            //console.log(`right input -> ${e.x}:${e.y}`);
            this.game_state = GameState.Normal;
        }

        private getInputTile(x: number, y: number): Point {
            var tile = 48;
            x -= this.root.offsetLeft;
            y -= this.root.offsetTop;
            return { x: Math.floor(x / tile), y: Math.floor(y / tile) };
        }

        private running(): void {
            window.setTimeout(() => { this.drawFrame() }, 500); // delay 500ms to load artifacts
        }

        private cleanup(): void {
            console.info('Game is cleaning up');
        }

        private drawFrame(): void {
            this.fps_counter++;

            this.drawBackground();
            this.drawTiles();
            this.drawHeroes();
            //this.drawText();
            this.drawFPS();
            window.requestAnimationFrame(() => { this.drawFrame() });
        }

        private drawText(): void {
            this.context.font = "1.5em Microsoft Yahei";
            this.context.fillStyle = 'blue';
            this.context.fillText(`Hello, ${this.counter}`, 100, 100);
        }

        private drawHeroes(): void {
            var tile = 48, pw = this.characters[0].width / 14, ph = this.characters[0].height,
                tt = Math.round((new Date().getTime() - this.init_time) / 200),
                state = tt % 3,
                deciding_state = tt % 2;

            for (var i = 0; i < this.cc.length; i++) {
                var h = this.cc[i];
                if (this.game_state == GameState.Deciding && this.selected_hero == i) {
                    this.context.drawImage(this.characters[h.pic],(deciding_state + 8) * pw, 0, pw, ph, h.x * tile, h.y * tile, tile, tile);
                } else {
                    this.context.drawImage(this.characters[h.pic], state * pw, 0, pw, ph, h.x * tile, h.y * tile, tile, tile);
                }
            }
        }

        private drawBackground(): void {
            //this.context.fillStyle = '#ffd800';
            //this.context.fillRect(0, 0, this.root.width, this.root.height);
        }

        private drawTiles(): void {
            var tile = 48;
            var a = 3;

            var mass = [
                2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
                5, 5, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
                7, 3, 1, 2, 0, 0, 2, 2, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0,
                7, 7, 1, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0,
                7, 7, 1, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
                7, 7, 1, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
                7, 7, 5, 2, 0, 0, 0, 0, 2, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0,
                7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
                7, 7, 1, 2, 0, 0, 0, 0, 2, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0,
                7, 7, 1, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
                1, 4, 1, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
                1, 4, 1, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
                5, 4, 1, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2,
                7, 7, 1, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2,
                5, 5, 5, 2, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
                2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2
            ];

            //console.debug(`mass.length=${mass.length}`);

            if (this.tiles == null) {
                for (var x = 0; x < 20; x++) {
                    for (var y = 0; y < 16; y++) {
                        var idx = mass[x + y * 20];
                        this.context.drawImage(this.bg_tile[idx], x * tile, y * tile, tile, tile);
                    }
                }
                this.tiles = this.context.getImageData(0, 0, this.root.width, this.root.height);
                console.debug('tiles saved');
            } else {
                this.context.putImageData(this.tiles, 0, 0);
            }
        }

        private drawFPS(): void {
            //var fps_current = new Date().getTime(),
            //    fps = Math.round(1000 / (fps_current - this.fps_previous));
            //this.fps_previous = fps_current;

            this.context.font = "0.8em Calibri";
            this.context.fillStyle = 'white';
            this.context.fillText(`FPS: ${this.fps}`, this.root.width - 50, 15);
        }


        private initMusic(): void {
            var context = new AudioContext();
            //var dogBarkingBuffer;

            function loadDogSound(url) {
                //var buff = localStorage.getItem(url);
                //if (buff) {
                //    context.decodeAudioData(buff, function (buffer) {
                //        //dogBarkingBuffer = buffer;

                //        playSound(buffer);
                //    }, function () {
                //            console.log("error decoding mp3");
                //        });

                //    return;
                //}


                var request = new XMLHttpRequest();
                request.open('GET', url, true);
                request.responseType = 'arraybuffer';

                // Decode asynchronously
                request.onload = function () {

                    //localStorage.setItem(url, request.response);

                    context.decodeAudioData(request.response, function (buffer) {
                        //dogBarkingBuffer = buffer;

                        playSound(buffer);
                    }, function () {
                            console.log("error decoding mp3");
                        });
                };

                request.onerror = function () {
                    console.log("error loading mp3");
                };

                request.send();
            }

            function playSound(buffer) {
                var source = context.createBufferSource(); // creates a sound source
                source.buffer = buffer;                    // tell the source which sound to play
                source.connect(context.destination);       // connect the source to the context's destination (the speakers)
                source.start(0);                           // play the source now
                // note: on older systems, may have to use deprecated noteOn(time);
            }

            loadDogSound("res/bgm/main.mp3");
        }
    }

    class App {
        public static main(): void {
            window.setTimeout(() => {
                new Game(<HTMLCanvasElement>document.getElementById('primary')).run();
            }, 100);
        }
    }

    App.main();
}